/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package pkg1;

import java.util.ArrayList;
import java.util.Random;
import javax.swing.JOptionPane;

/**
 * An object representing the domain model for the floor view.
 * This object is used to give the floorTemplate the information it
 * needs
 * @author Nick
 */
public class FloorContext {
    private ArrayList<Bed> beds = new ArrayList<Bed>();
    private ArrayList<Patient> patients = new ArrayList<Patient>();
    private ArrayList<Room> rooms = new ArrayList<Room>();
    private ArrayList<Observer> observers = new ArrayList<Observer>();
    private Context context = Context.getContext();
    private int numberOfAnnonymousPatients = 0;
    private String id;
    private boolean changed = false;

    public FloorContext(String id) {
        this.id = id;
    }
    
    /**
     * Adds an observer to this model
     * @param o the observer
     */
    public void addObserver(Observer o) {
        observers.add(o);
    }
    
    /**
     * removes an observer from this model
     * @param o the observer
     */
    public void removeObserver(Observer o) {
        if(observers.contains(o)) {
            observers.remove(o);
        }
    }
    
    /**
     * notifies all of the attached observers.
     */
    public void notifyAllObservers() {
        for(Observer observer : observers) {
            observer.update(normalBedsFullAndBedCEmpty(), isRoomCInSpecialCareMode());
        }
    }
    
    /**
     * sets the number of annonymous patients associated with the room.
     * @param number 
     */
    public void setNumberOfAnnonymousPatients(int number) {
        numberOfAnnonymousPatients = number;
    }
    
    /**
     * gets the number of annon patients
     * @return 
     */
    public int getNumberOfAnnonymousPatients() {
        return numberOfAnnonymousPatients;
    }
    
    /**
     * increments the number of anon patients
     */
    public void incrementNumberOfAnnonymousPatients() {
        numberOfAnnonymousPatients++;
    }
    /**
     * @return the beds
     */
    public ArrayList<Bed> getBeds() {
        return beds;
    }
    
    /**
     * @return the patients
     */
    public ArrayList<Patient> getPatients() {
        return patients;
    }

    /**
     * @return the rooms
     */
    public ArrayList<Room> getRooms() {
        return rooms;
    }
    
    /**
     * sets the rooms
     * @param rooms 
     */
    public void setRooms(ArrayList<Room> rooms) {
        this.rooms = rooms;
    }
    
    /**
     * sets the beds
     * @param beds 
     */
    public void setBeds(ArrayList<Bed> beds) {
        this.beds = beds;
    }
    
    /**
     * moves the specified patient to the specified bed.
     * @param p the patient
     * @param newBed the bed
     * @return whether or not the move was allowed and made.
     */
    public boolean movePatient(Patient p, Bed newBed) {
        if(!p.isCritical() && !newBed.isCriticalCare() ||
                p.isCritical() && newBed.isCriticalCare() ) {
            Bed currentBed = getPatientsBed(p);
            context.getFloor(currentBed).dismiss(p, currentBed);
            context.getFloor(newBed).admit(p, context.getRandomDoctor(), context.getRandomNurse(), newBed);
            if(!(context.getFloor("emergency room") == null)) {
                context.getFloor("emergency room").notifyAllObservers();
            }
            return true;
        }
        return false;
    }
    
    /**
     * returns the bed of the specified patient
     * @param p the patient
     * @return his/her bed
     */
    private Bed getPatientsBed(Patient p) {
        for(Bed b : beds) {
            if(!b.isAvailable() && b.getPatient() != null && b.getPatient().equals(p)) {
                return b;
            }
        }
        return null;
    }
    
    /**
     * returns whether or not the floor context has been modified (used for saving purposes)
     * @return 
     */
    public boolean getChanged() {
        return changed;
    }
    
    /**
     * sets the changed state
     * @param b 
     */
    public void setChanged(boolean b) {
        changed = b;
    }
    
    /**
     * admits a patient to a bed
     * @param p the patient
     * @param d the patient's doctor
     * @param n the patient's nurse
     * @param b the patients bed
     */
    public void admit(Patient p, Doctor d, Nurse n, Bed b) {
        context.removePatientFromDoctorAndNurseIfAlreadyExist(p);
        d.getPatients().add(p);
        n.getPatients().add(p);
        b.assignPatient(p);
        patients.add(p);
        if(!(context.getFloor("emergency room") == null)) {
            context.getFloor("emergency room").notifyAllObservers();
        }
    }
    
    /**
     * sets the Id of the floorcontext
     * @param id 
     */
    public void setId(String id) {
        this.id = id;
    }
    
    /**
     * gets the Id of the floorContext
     * @return 
     */
    public String getId() {
        return id;
    }
    
    /**
     * gets the Beds of the floorContext
     * @param bedId
     * @return 
     */
    public Bed getBed(int bedId) {
        for(Bed bed : beds) {
            if(bed.getId() == bedId) {
                return bed;
            }
        }
        return null;
    }
    
    /**
     * dismisses a patient from the hospital floor
     * @param p the patient
     * @param b the bed of the patient
     */
    public void dismiss(Patient p, Bed b) {
        patients.remove(p);
        removePatientFromDoctor(p);
        removePatientFromNurse(p);
        removePatientFromBed(b);
        if(!(context.getFloor("emergency room") == null)) {
            context.getFloor("emergency room").notifyAllObservers();
        }
    }
    
    /**
     * removes the patient from the specified bed
     * @param b the bed
     */
    private void removePatientFromBed(Bed b) {
        for(Bed bed : beds) {
            if(bed.equals(b)) {
                b.dismissPatient();
                return;
            }
        }
        if(!(context.getFloor("emergency room") == null)) {
            context.getFloor("emergency room").notifyAllObservers();
        }
    }
    
    /**
     * removes the specified patient from his/her nurse
     * @param p the patient
     */
    private void removePatientFromNurse(Patient p) {
        for(Nurse nurse : context.getNurses()) {
            if(nurse.getPatients().contains(p)) {
                nurse.getPatients().remove(p);
                return;
            }
        }
    }
    
    /**
     * removes the specified patient from his/her Doctor
     * @param p the patient
     */
    private void removePatientFromDoctor(Patient p) {
        for(Doctor doctor : context.getDoctors()) {
            if(doctor.getPatients().contains(p)) {
                doctor.getPatients().remove(p);
                return;
            }
        }
        
    }
    
    /**
     * returns the doctor of the specified patient
     * @param p the patient
     * @return d the doctor
     */
    public Doctor getDoctor(Patient p) {
        for(Doctor doc : context.getDoctors()) {
            if(doc.getPatients().contains(p)) {
                return doc;
            }
        }
        return null;
    }
    
    /**
     * returns the nurse of the specified patient
     * @param p the patient
     * @return n the nurse
     */
    public Nurse getNurse(Patient p) {
        for(Nurse nurse : context.getNurses()) {
            if(nurse.getPatients().contains(p)) {
                return nurse;
            }
        }
        return null;
    }
    
    /**
     * creates a unique patientId for a newly admitted patient
     * @return id the patients ID.
     */
    public int createNewPatientId() {
        Random random = new Random();
        int newID = 1;
        while(!isUnique(newID)) {
            newID = random.nextInt(20000);
        }
        return newID;
    }
    
    /**
     * a helper method for createNewPatientId.
     * tells us whether a given ID is unique.
     * @param newPatientID the ID in question
     * @return whether or not it is unique
     */
    private boolean isUnique(int newPatientID) {
        for(Patient patient : patients) {
            if(patient.getId() == newPatientID) {
                return false;
            }
        }
        return true;
    }
    
    /**
     * the state of the context. Whether or not all the normal beds not
     * in special care rooms are full and Bed C is empty.
     * @return 
     */
    public boolean normalBedsFullAndBedCEmpty() {
        for(Bed bed : beds) {
            if(!bed.isNormalBedInSpecialCare() && !bed.isCriticalCare() && bed.isAvailable()){
                return false;
            }
            if(bed.getId() == 102) {
                if(!bed.isAvailable()) {
                    return false;
                }
            }
        }
        return true;
    }
    
    /**
     * the state of the context. Whether or not the last special care room
     * is in special care mode.
     * @return 
     */
    public boolean isRoomCInSpecialCareMode() {
        for(Bed bed : beds) {
            if(bed.isNormalBedInSpecialCare() && !bed.isAvailable()) {
                return false;
            }
        }
        return true;
    }
}
